package cn.edu.jxau.core;

import cn.edu.jxau.core.annotation.Controller;
import cn.edu.jxau.core.annotation.Inject;
import cn.edu.jxau.core.annotation.Service;
import cn.edu.jxau.core.annotation.Aspect;
import cn.edu.jxau.core.aop.AspectTemplate;
import cn.edu.jxau.core.aop.Proxy;
import cn.edu.jxau.core.aop.ProxyFactory;
import cn.edu.jxau.core.exception.FrameException;
import cn.edu.jxau.core.util.ClassUtils;
import cn.edu.jxau.core.util.ReflectUtils;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.*;

/**
 * Desc:
 * ------------------------------------
 * Author:fulei04@meituan.com
 * Date:2017/12/15
 * Time:下午2:10
 */
public class ApplicationContext {

    private static Set<Class<?>> CLASS_SET;
    private static Map<Class, Object> BEAN_MAP;

    static {

        try {
            //搜集BASE_PACKAGE指定包下，所有字节码对象 //
            CLASS_SET = ClassUtils.getClassSet(Constant.BASE_PACKAGE);

            // 构造Bean map //
            BEAN_MAP = getBeanMap();

            // 将代理类加入BEAN_MAP中 //
            Map<Class, List<Proxy>> proxyMap = getProxyMap();
            for (Map.Entry<Class, List<Proxy>> entry : proxyMap.entrySet()) {
                Class targetClass = entry.getKey();
                List<Proxy> proxyList = entry.getValue();
                addBean(targetClass, ProxyFactory.createProxy(targetClass, proxyList)); //加入容器
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private ApplicationContext() {
        throw new IllegalArgumentException("不可实例化ApplicationContext");
    }

    public static Set<Class> getServices() {
        return getBeanClassByAnnotationClass(Service.class);
    }

    public static Set<Class> getControllers() {
        return getBeanClassByAnnotationClass(Controller.class);
    }

    private static Set<Class> getAspects() {
        return getBeanClassByAnnotationClass(Aspect.class);
    }

    /**
     * 获取标识了指定注解的自己Class
     *
     * @return
     */
    private static Set<Class> getBeanClassByAnnotationClass(Class<? extends Annotation> annotationClass) {

        Set<Class> classSet = new HashSet<>();
        for (Class clazz : CLASS_SET) {
            if (clazz.isAnnotationPresent(annotationClass)) {
                classSet.add(clazz);
            }
        }
        return classSet;
    }

    /**
     * 获取切面的目标字节码对象
     *
     * @param aspect
     * @return
     */
    private static Set<Class> getAspectTargetClass(Aspect aspect) {

        Class<? extends Annotation> annoClass = aspect.value();
        return getBeanClassByAnnotationClass(annoClass); //获取标识了annoClass注解的所有Class
    }

    /**
     * 构造【代理类】与【目标类】的映射结构
     *
     * @return
     */
    private static Map<Class, Set<Class>> getTargetClassMap() {

        Map<Class, Set<Class>> proxyMap = new HashMap<>();
        Set<Class> aspectClassSet = getAspects();
        for (Class aspectClass : aspectClassSet) {
            if (!AspectTemplate.class.isAssignableFrom(aspectClass)) {
                throw new FrameException(aspectClass.getName() + "用@Aspect标识，但未实现AspectTemplate类");
            }
            Aspect aspect = (Aspect) aspectClass.getAnnotation(Aspect.class);
            Set<Class> aspectTargetSet = getAspectTargetClass(aspect);
            proxyMap.put(aspectClass, aspectTargetSet);
        }
        return proxyMap;
    }

    /**
     * 构造【目标类】与【代理对象】的映射结构
     *
     * @return
     */
    private static Map<Class, List<Proxy>> getProxyMap() throws IllegalAccessException, InstantiationException {

        Map<Class, Set<Class>> targetClassMap = getTargetClassMap();
        Map<Class, List<Proxy>> proxyMap = new HashMap<>();
        for (Map.Entry<Class, Set<Class>> entry : targetClassMap.entrySet()) {
            Class aspectClass = entry.getKey(); //切面
            Set<Class> targetClassSet = entry.getValue(); //目标
            for (Class targetClass : targetClassSet) {
                Proxy proxy = (Proxy) aspectClass.newInstance();
                if (proxyMap.containsKey(targetClass)) {
                    proxyMap.get(targetClass).add(proxy);
                } else {
                    List<Proxy> list = new ArrayList<>();
                    list.add(proxy);
                    proxyMap.put(targetClass, list);
                }
            }
        }
        return proxyMap;
    }

    /**
     * 字节码对象与其实例的映射结构
     *
     * @return
     */
    private static Map<Class, Object> getBeanMap() {

        Map<Class, Object> beanMap = new HashMap();
        for (Class clazz : getAllBeanClass()) {
            beanMap.put(clazz, ReflectUtils.newInstance(clazz));
        }
        dependencyInject(beanMap); //依赖注入
        return beanMap;
    }

    private static Set<Class> getAllBeanClass() {

        Set<Class> beanClassSet = new HashSet<>();
        beanClassSet.addAll(getControllers());
        beanClassSet.addAll(getServices());
        return beanClassSet;
    }

    public static <T> T getBean(Class<T> clazz) {
        return (T) BEAN_MAP.get(clazz);
    }

    private static void dependencyInject(Map<Class, Object> beanMap) {

        if (beanMap == null) {
            throw new FrameException("容器为null");
        }
        if (beanMap.isEmpty()) {
            return; //容器中无有效bean
        }
        for (Map.Entry<Class, Object> entry : beanMap.entrySet()) {
            Class<?> clazz = entry.getKey();
            Field[] fieldArr = clazz.getDeclaredFields();
            for (Field field : fieldArr) {
                if (field.isAnnotationPresent(Inject.class)) {
                    ReflectUtils.setField(field, entry.getValue(), beanMap.get(field.getType()));
                }
            }
        }
    }

    private static void addBean(Class<?> clazz, Object obj) {
        BEAN_MAP.put(clazz, obj);
    }


}
